using Mono.Cecil.Cil;
using static System.String;

namespace Cil.Ast
{
    public enum LoopType
    {
        While,
        DoWhile
    }

    public sealed class LoopExpression : BranchingExpression
    {
        public const int ConditionPos = 0;
        public const int BodyExpressionPos = 1;
        public const int MaxNodes = 2;

        public LoopExpression(Context context, Instruction? incomingJump, Instruction loopJump, LoopType loopType,
            ConditionExpression condition, Expression body) : base(context, loopJump)
        {
            Nodes = new FixedList<Expression>(MaxNodes);
            if (incomingJump != null)
                Context.SetExpression(incomingJump, this);
            Condition = condition;
            Body = body;
            LoopType = loopType;
        }

        public LoopType LoopType { get; private set; }

        public ConditionExpression Condition
        {
            get => (ConditionExpression)Nodes[ConditionPos];
            private set
            {
                Nodes[ConditionPos] = value;
                value.ParentNode = this;
            }
        }

        public Expression Body
        {
            get => Nodes[BodyExpressionPos];
            private set
            {
                Nodes[BodyExpressionPos] = value;
                value.ParentNode = this;
            }
        }

        public override string ToString()
        {
            var sb = new System.Text.StringBuilder();

            if (LoopType == LoopType.DoWhile)
            {
                // If Condition contains 
                if (Condition.IsGroup)
                {
                    var lastIndex = Condition.Nodes.Count - 1;
                    sb.AppendLine("do {");
                    sb.AppendLine(Body.ToString());
                    sb.AppendLine(Join(Environment.NewLine,
                        Condition.TakeWhile((_, index) => index != lastIndex)
                            .Select(node => node.ToString())));
                    sb.AppendLine($"}} while ({Condition.Last()});");
                }
                else
                {
                    sb.AppendLine("do {");
                    sb.AppendLine(Body.ToString());
                    sb.AppendLine(Format("}} while ({0});", Condition));
                }
            }
            else
            {
                sb.AppendLine(Format("while ({0}) {{", Condition));
                sb.AppendLine(Body.ToString());
                sb.AppendLine("}");
            }

            return sb.ToString();
        }
    }
}